import json

from fastapi import APIRouter

from app.dongcai.apis import *
from app.dongcai.history import history
from app.dongcai.items import *
from app.dongcai.mongo_cols import *
from db.redis_client import sync_cli as redis_cli

router = APIRouter(prefix='/dongcai/api')


@router.get("/suggest")
def suggest():
    # 涨幅2-6，创业板2-12
    # 活跃度不够，量比1-3之间
    # 流通市值 50-500
    # 换手 大于3
    # 突破5日均线
    # 今日放量上涨
    # 连续3天放量
    choice_params = ChoiceParams(**choice_params_col.find_one({}))
    history.update()
    results = []
    for stock_item in get_all_stocks():
        sorted_stock = history.get_data(stock_item.symbol)
        if sorted_stock and len(sorted_stock) > 20:
            if choice_params.min_change_rate_20 is not None:
                _min = sorted_stock[19]['price'] * choice_params.min_change_rate_20
                if not all([_['price'] > _min for _ in sorted_stock[:19]]):
                    continue
            if choice_params.max_change_rate_20 is not None:
                _max = sorted_stock[19]['price'] * choice_params.max_change_rate_20
                if not all([_['price'] < _max for _ in sorted_stock[:19]]):
                    continue


            if choice_params.volume_rate is not None and stock_item.volume_rate < choice_params.volume_rate:
                # 量比小于1的不要
                continue
            if choice_params.min_change_rate is not None and stock_item.change_rate < choice_params.min_change_rate:
                # 最小涨幅
                continue
            if choice_params.max_change_rate is not None and stock_item.change_rate >= choice_params.max_change_rate:
                # 最大涨幅
                continue
            if choice_params.min_change_hand_rate is not None and stock_item.change_hand_rate < choice_params.min_change_hand_rate:
                # 换手小于5的不要
                continue

            if choice_params.max_change_hand_rate is not None and stock_item.change_hand_rate > choice_params.max_change_hand_rate:
                continue

            if choice_params.min_l_market_value is not None and stock_item.l_market_value < choice_params.min_l_market_value:
                # 最小流通市值
                continue
            if choice_params.max_l_market_value is not None and stock_item.l_market_value > choice_params.max_l_market_value:
                # 最大流通市值
                continue

            _ma5 = round((sum([_['price'] for _ in sorted_stock[:4]]) + stock_item.price) / 5, 2)
            _ma10 = round((sum([_['price'] for _ in sorted_stock[:9]]) + stock_item.price) / 10, 2)
            _ma15 = round((sum([_['price'] for _ in sorted_stock[:14]]) + stock_item.price) / 15, 2)
            _ma20 = round((sum([_['price'] for _ in sorted_stock[:19]]) + stock_item.price) / 20, 2)

            _max_val5 = round(max([_['trade_amount'] for _ in sorted_stock[:4]]), 2)
            _max_val10 = round(max([_['trade_amount'] for _ in sorted_stock[:9]]), 2)
            _max_val15 = round(max([_['trade_amount'] for _ in sorted_stock[:14]]), 2)
            _max_val20 = round(max([_['trade_amount'] for _ in sorted_stock[:19]]), 2)

            vr1 = stock_item.trade_amount / sorted_stock[0]['trade_amount']
            if choice_params.min_vr is not None and vr1 < choice_params.min_vr:
                # 放量小于1的不要
                continue
            if choice_params.max_vr is not None and vr1 > choice_params.max_vr:
                # 放量大于3的不要
                continue

            if sorted_stock[0]['change_rate'] >= 0:
                continue

            if choice_params.min_up_line is not None and stock_item.highest_price / stock_item.price > choice_params.min_up_line:
                continue

            if stock_item.price <= stock_item.open_price:
                continue

            if stock_item.price <= sorted_stock[0]['highest_price']:
                continue

            item = stock_item.dict()
            item.update({
                "volume_rate": stock_item.volume_rate,
                "market_value": stock_item.market_value,
                "l_market_value": stock_item.l_market_value,
                "up_line": (stock_item.highest_price / stock_item.price - 1) * 100,
                "down_line": (stock_item.lowest_price / stock_item.open_price - 1) * 100,
                "ma5": _ma5,
                "ma10": _ma10,
                "ma15": _ma15,
                "ma20": _ma20,
                "max_val5": _max_val5,
                "max_val10": _max_val10,
                "max_val15": _max_val15,
                "max_val20": _max_val20,
            })
            item.update({"vr": round(vr1, 2)})
            results.append(item)
    return results


def get_all_stocks():
    k_line = get_k_lines("1.000001")
    date_str = k_line['data']['klines'][-1].split(",")[0]
    page_size = 100
    page_num = 1
    while 1:
        data = get_all(page_size,page_num)
        total = data['data']['total']


        for item in data['data']['diff']:
            try:
                stock_item = StockItemV2(date=date_str, **item)
            except:
                ...
            else:
                yield stock_item

        if page_size * page_num >= total:
            break

        page_num += 1

def clac_m(start_index, day: int, item_list: list[StockItemV2]):
    x = 0
    for i in range(start_index, start_index + day):
        x += item_list[i].price

    return round(x / day, 2)


def get_history(item: StockItemV2):
    n = stocks_v2_col.count_documents({'symbol': item.symbol})
    if n < 30:
        data = get_k_lines(f"{item.market_type}.{item.symbol}")
        bulk_write = []
        k_list = []
        for k_line in data['data']['klines']:
            d = k_line.split(",")
            s_item = StockItemV2(
                date=d[0],
                name=data['data']['name'],
                symbol=data['data']['code'],
                price=float(d[2]),
                market_type=data['data']['market'],
                highest_price=float(d[3]),
                lowest_price=float(d[4]),
                open_price=float(d[1]),
                change_rate=float(d[8]),
                change_amount=float(d[9]),
                volume=float(d[5]),
                trade_amount=float(d[6]),
                diff_rate=float(d[7]),
                change_hand_rate=float(d[10]),
            )
            k_list.append(s_item)

        sort_k_list = sorted(k_list, key=lambda k: k.date, reverse=True)
        if len(sort_k_list) != 120:
            return
        for index in range(90):
            insert_item = sort_k_list[index]
            insert_item.m5 = clac_m(index, 5, sort_k_list)
            insert_item.m10 = clac_m(index, 10, sort_k_list)
            insert_item.m20 = clac_m(index, 20, sort_k_list)
            insert_item.m30 = clac_m(index, 30, sort_k_list)
            bulk_write.append(insert_item.dict())

        stocks_v2_col.delete_many({'symbol': item.symbol})
        stocks_v2_col.insert_many(bulk_write)
    else:
        k_list = [
            StockItemV2(**_) for _ in stocks_v2_col.find({"symbol": item.symbol}).sort('date', -1).limit(30) if
            _['date'] != item.date
        ]
        if k_list:
            if stocks_v2_col.find_one({"symbol": item.symbol, "date": item.date}):
                return
            k_list.insert(0, item)
            sort_k_list = sorted(k_list, key=lambda k: k.date, reverse=True)
            item.m5 = clac_m(0, 5, sort_k_list)
            item.m10 = clac_m(0, 10, sort_k_list)
            item.m20 = clac_m(0, 20, sort_k_list)
            item.m30 = clac_m(0, 30, sort_k_list)
            stocks_v2_col.update_one(
                {"symbol": item.symbol, "date": item.date},
                {"$set": item.dict()},
                upsert=True
            )

@router.get("/update_latest_stocks")
def update_all_stocks():
    for item in get_all_stocks():
        get_history(item)


def init_history():
    cursor = stocks_col.aggregate([
        {
            "$group": {
                "_id": {
                    "symbol": "$symbol",
                    "market_type": "$market_type"
                },
                "count": {"$sum": 1}
            }
        }
    ])
    for item in cursor:
        if item['count'] < 120:
            update_history("{}.{}".format(
                item["_id"]['market_type'],
                item["_id"]['symbol']
            ))


def update_history(code):
    data = get_k_lines(code)
    bulk_write = []
    for k_line in data['data']['klines']:
        d = k_line.split(",")
        try:
            s_item = StockItem(
                date=d[0],
                name=data['data']['name'],
                symbol=data['data']['code'],
                price=float(d[2]),
                market_type=data['data']['market'],
                highest_price=float(d[3]),
                lowest_price=float(d[4]),
                open_price=float(d[1]),
                change_rate=float(d[8]),
                change_amount=float(d[9]),
                volume=float(d[5]),
                trade_amount=float(d[6]),
                diff_rate=float(d[7]),
                change_hand_rate=float(d[10]),
            )
        except Exception as e:
            print(e)
        else:
            bulk_write.append(s_item.dict())
    print("completed", data['data']['name'])
    stocks_col.delete_many({"symbol": code, "date": {"$gte": bulk_write[0]["date"], "$lte": bulk_write[-1]["date"]}})
    stocks_col.insert_many(bulk_write)


def get_cache(symbol):
    redis_key = f"zt_cache:{symbol}"
    results = redis_cli.get(redis_key)
    if results:
        return ZTItem(**json.loads(results))
    return None


def set_cache(item: ZTItem, exat):
    redis_key = f"zt_cache:{item.symbol}"
    redis_cli.set(redis_key, item.json(), exat=exat)


def is_trade_day():
    now = datetime.datetime.now()
    res = redis_cli.get(f"is_trade_day:{now.strftime('%Y-%m-%d')}")
    if res is not None:
        if res == "1":
            return True
        else:
            return False
    else:
        k_line = get_k_lines("1.000001")
        date_str = k_line['data']['klines'][-1].split(",")[0]
        if now.strftime('%Y-%m-%d') == date_str:
            redis_cli.set(f"is_trade_day:{date_str}", "1", ex=24 * 3600)
            return True
        else:
            redis_cli.set(f"is_trade_day:{date_str}", "0", ex=24 * 3600)
            return False


def zt_his():
    if not is_trade_day():
        return
    data = get_zt_pool()
    today = datetime.datetime.now().replace(hour=23, minute=0, second=0, microsecond=0)
    exat = int(today.timestamp())
    codes = []

    for d in data['data']['pool']:
        item = ZTItem(**d, **d['zttj'])

        if item.fbt < "09:30:00":
            continue
        if item.fbt > "10:30:00":
            continue
        if item.change_rate > 11:
            continue
        if item.days == 1 and item.ct == 1:
            cache_item = get_cache(item.symbol)
            if cache_item:
                item.diff_amount = item.trade_amount - cache_item.trade_amount
                item.diff_fund = item.fund - cache_item.fund
                d = item.diff_amount + item.diff_fund
                if d > 0:
                    item.new_fund = d + cache_item.new_fund
                elif d < 0:
                    item.cancel_fund = d + cache_item.cancel_fund
                else:
                    item.new_fund = cache_item.new_fund
                    item.cancel_fund = cache_item.cancel_fund
            # todo 有问题掉板了的数据没更新
            set_cache(item, exat)
            codes.append(f"{item.market_type}.{item.symbol}")

    # todo 这里的少了掉板的数据
    redis_cli.set(
        f"zt_codes:{today.strftime('%Y-%m-%d')}",
        ",".join(codes),
        exat=int((today + datetime.timedelta(days=1)).timestamp())
    )


@router.get("/yesterday_zt")
def yesterday_zt():
    k_line = get_k_lines("1.000001")
    date_str = k_line['data']['klines'][-1].split(",")[0]
    last_trade_date_str = k_line['data']['klines'][-2].split(",")[0]
    codes = redis_cli.get(f"zt_codes:{last_trade_date_str}")
    results = []
    if codes:
        data = get_symbol_data(codes)
        for _, d in data['data']['diff'].items():
            item = StockItem(**d, date=date_str)
            sorted_stock = history.get_data(item.symbol)
            item.change_amount = round(item.change_amount / 100, 2)
            item.change_hand_rate = round(item.change_hand_rate / 100, 2)
            item.change_rate = round(item.change_rate / 100, 2)
            item.diff_rate = round(item.diff_rate / 100, 2)
            item.highest_price = round(item.highest_price / 100, 2)
            item.lowest_price = round(item.lowest_price / 100, 2)
            item.open_price = round(item.open_price / 100, 2)
            item.price = round(item.price / 100, 2)
            item.speed = round(item.speed / 100, 2)
            item.volume_rate = round(item.volume_rate / 100, 2)

            _ma5 = round((sum([_['price'] for _ in sorted_stock[:4]]) + item.price) / 5, 2)
            _ma10 = round((sum([_['price'] for _ in sorted_stock[:9]]) + item.price) / 10, 2)
            _ma15 = round((sum([_['price'] for _ in sorted_stock[:14]]) + item.price) / 15, 2)
            _ma20 = round((sum([_['price'] for _ in sorted_stock[:19]]) + item.price) / 20, 2)

            _max_val5 = round(max([_['trade_amount'] for _ in sorted_stock[:4]]), 2)
            _max_val10 = round(max([_['trade_amount'] for _ in sorted_stock[:9]]), 2)
            _max_val15 = round(max([_['trade_amount'] for _ in sorted_stock[:14]]), 2)
            _max_val20 = round(max([_['trade_amount'] for _ in sorted_stock[:19]]), 2)
            g = item.dict()
            g.update({
                "ma5": _ma5,
                "ma10": _ma10,
                "ma15": _ma15,
                "ma20": _ma20,
                "max_val5": _max_val5,
                "max_val10": _max_val10,
                "max_val15": _max_val15,
                "max_val20": _max_val20,
                "l_market_value": item.l_market_value,
                "market_value": item.market_value,
            })
            results.append(g)

    return results


@router.get('/zt')
def get_zt():
    keys = redis_cli.keys("zt_cache:*")
    results = []
    for k in keys:
        results.append(json.loads(redis_cli.get(k)))
    return sorted(results, key=lambda x: x['fbt'])


@router.post('/zt_record')
def post_zt_record(item: ZTItem):
    shou_ban_col.update_one({
        "symbol": item.symbol
    }, {"$setOnInsert": item.dict()}, upsert=True)

# 1）二板缩量一字，打出题材或者个股辨识度，同时代表主力非常强悍。（缩量代表主力锁仓）
# 2）隔天放量大阴棒交换筹码，洗盘还是出货？
# 3）阴线要求是小幅平开且无长上影线。（这是关键）
# 4）第3天回踩5日线，或者13天线回手一掏。
# 以上4个条件缺一不可，尤其要理解第3条，这个可以判断主力是洗盘还是出货。

if __name__ == '__main__':
    init_history()
